-- ActivityBossM
-- Created by cheny3 Jun/17/2016
-- 活动副本BOSS模块

module("ActivityBossM", package.seeall);

local PURE_FILE = "ActivityBossM";

-- 内部函数
local startDungeon;
local bossDie;
local whenStartRound;
local whenEndRound;

-- 配置表
local activityBossTable    = {};

-- 模块初始化操作
function init()
    activityBossTable = {};
    -- 活动副本BOSS配置
    local activity_boss_CSV = get_activity_boss_CSV();
    for i = 1, #activity_boss_CSV do
        local id = activity_boss_CSV[i].id;
        activityBossTable[id] = activity_boss_CSV[i];
    end
    activity_boss_CSV = nil;

    -- 移除所有关注
    EventMgr.removeAll("ActivityBossM");
    -- 关注回合开始的事件
    CombatM.registerStartRoundCB("activity_boss_start_round", whenStartRound);

    -- 关注回合而结束的事件
    CombatM.registerEndRoundCB("energy_platform_end_round", whenEndRound);

    EventMgr.register("ActivityBossM", event.START_ROUND, function(args)
        -- 纯粹为了模块加载
    end);

    DungeonServiceM.registerStartDungeonCB("activity_boss", startDungeon);

    -- BOSS死亡
    EventMgr.register("ActivityBossM", event.BOSS_DIE, bossDie);
end

-- 检索副本BOSS
function queryBoss(classId, path)
    if not activityBossTable[classId] then
        return nil;
    end

    if path == nil or path == "" then
        return table.deepcopy(activityBossTable[classId]);
    end

    return table.deepcopy(activityBossTable[classId][path]);
end

-- 获取战斗的BOSS
function getCombatSlime()
    local dungeonId = DungeonM.getDungeonId();
    local info = ActivityDungeonM.getActivityByDungeonId(dungeonId);
    if not info then
        return nil;
    end

    local pos = info["boss_pos"] + 1;    -- 客户端下标多1

    if UnrealDungeonM.isInUnreal() then
        -- 夹层中
        local unrealId = UnrealDungeonM.getUnrealId();
        local dbase = UnrealDungeonM.query(unrealId, "dbase");
        if type(dbase["boss_pos"]) == "number" then
            pos = dbase["boss_pos"] + 1;
        end
    end

    local grid = DungeonM.getGridByPos(pos);
    return grid.monster;
end

-- 获取存活的敌方史莱姆列表
function getAliveEnemySlimes()
    if not ActivityDungeonM.isInActivityBossLayer() then
        -- 不在副本boss层
        return {};
    end

    local grids = DungeonM.getCurrentDungeon();
    local ret = {};
    for _, grid in pairs(grids) do
        repeat
            if not grid:isMonster() or not grid.monster or grid.monster:isDead() then
                -- 过滤非怪物，或者已经死亡的怪物
                break;
            end

            if grid.monster:isSlime() or isSpecialBoss(grid.monster) then
                table.insert(ret, grid.monster);
            end
        until true
    end

    return ret;
end

-- 有配普通攻击的BOSS
function isSpecialBoss(who)
    if not who:isBoss() then
        return false;
    end

    local classId = who.classId;
    local info = queryBoss(classId) or {};

    if type(info["physic_skill"]) ~= "number" then
        -- 没配普通攻击，不在范围内
        return false;
    end

    return true;
end

-- 是否是主动技能
function isActiveSkill(slime, skillId)
    local activeSkillId = queryBoss(slime.classId, "active_skill");
    if type(activeSkillId) == "number" then
        return activeSkillId == skillId;
    end
    return false;
end

-- 回合开始的回调
whenStartRound = function()
    prepareAttack();
end

-- 回合结束回调
whenEndRound = function()
    energyPlatformTrigger();
end

-- 史莱姆怪自动攻击
function prepareAttack()
    if not ActivityDungeonM.isInActivityBossLayer() then
        -- 不在副本boss层
        return;
    end

    local slime = getCombatSlime();
    if not slime or slime:isDead() then
        -- 对手史莱姆已经死亡
        return;
    end

    -- 玩家已经死亡
    if ME.user:isDead() then
        return;
    end

    -- 获取存活的敌法史莱姆
    local slimes = getAliveEnemySlimes();
    if #slimes == 0 then
        return;
    end

    for _, slime in pairs(slimes) do
        local user = ME.user;

        -- 判断能否攻击玩家
        local ret = FormulaM.invoke("CAN_SLIME_MONSTER_ATTACK", user, slime);

        if true == ret then
            local classId = slime.classId;
            local skillType = queryBoss(classId, "active_type");

            -- 优先使用主动技能
            local skillId = queryBoss(classId, "active_skill");
            if type(skillId) == "number" then
                local remainCD = getRemainSkillCD(slime);
                if remainCD <= 1 then
                    useSkill(slime, user, skillId);
                    if skillType == "magic_physic" then
                        -- 还需要触发物理攻击
                        physic(slime, user);
                    end
                    return true;
                end
            end

            -- 无法施放主动技能时，进行普通攻击
            physic(slime, user);
        else
            DungeonLogM.addLog(string.format("[ActivityBossM]%s无法攻击玩家，原因：%s", slime:getName(), tostring(ret)));
        end
    end

    return true;
end

-- 获取自动攻击轮次倒计时显示
function getAttackCountDown(slime)
    if #PropM.fetchProps(slime, "transfiguration") > 0 then
        -- 被变形了
        return nil;
    end

    local remainCD = getRemainSkillCD(slime);
    if type(remainCD) == "number" and remainCD <= 0 then
        return 1;
    end

    return remainCD;
end

-- 获取主动技能倒计时
function getRemainSkillCD(slime)
    local skillId = queryBoss(slime.classId, "active_skill");
    if type(skillId) ~= "number" then
        -- 没有主动技能
        return nil;
    end

    -- 上次施放技能回合
    local skillRound = slime.skill_round or 1;

    local prop = PropM.combine(slime, "add_cd", "slime");

    local round = CombatM.getRound();
    local cd = queryBoss(slime.classId, "cast_cycle");

    return math.max(cd + prop[3] - (round - skillRound), 0);
end

-- 使用主动技能（这里不进行条件判断）
function useSkill(slime, target, skillId)
    local round = CombatM.getRound();

    -- 记录施放技能的回合
    slime.skill_round = round + 1;

    DungeonLogM.addLog(string.format("[ActivityBossM](%s)使用技能%d攻击(%s)", slime:getName(), skillId, target:getName()));

    SkillM.apply(slime, target, skillId, round);

    return true;
end

-- 普通攻击
function physic(slime, target)
    local skillId = queryBoss(slime.classId, "physic_skill");
    if type(skillId) ~= "number" then
        -- 没有配置普通攻击
        return false;
    end

    DungeonLogM.addLog(string.format("[ActivityBossM](%s)对(%s)进行普通攻击", slime:getName(), target:getName()));

    -- 初始化序列
    SkillM.initSequence(slime, target, skillId);
    -- 远程怪攻击动作
    SkillM.getSequence(target):remoteAttack(slime, target, skillId);

    local round = CombatM.getRound();
    SkillM.apply(slime, target, skillId, round);

    return true;
end

-- 统计玩家进入活动BOSS层的次数
function statEnterBossLayerStat()
    if UnrealDungeonM.isInUnreal() then
        -- 夹层不管
        return;
    end

    if DungeonInstanceM.isInInstance() then
        -- 副本不管
        return;
    end

    local dungeonId = DungeonM.getDungeonId();
    local info = ActivityDungeonM.getActivityByDungeonId(dungeonId) or {};
    local dbase = info["dbase"] or {};
    local statInfo = dbase["layer_stat"];
    if not statInfo then
        -- 没有统计字段
        return;
    end

    local curLayer = DungeonM.currentLayer();
    local statLayer = statInfo["layer"];
    if statLayer ~= curLayer then
        -- 不是统计的层数
        return;
    end

    local key = statInfo["key"];
    local maxLimit = statInfo["max_limit"];
    local reset = statInfo["reset"] or 0;

    -- 当前的统计数量
    local count = BuildingBonusM.getFieldStat(key) + 1;
    if count > maxLimit then
        count = reset;
    end

    local slime = getCombatSlime();
    if slime then
        local classId = slime.classId;
        local bossInfo = queryBoss(classId, "dbase");
        if tonumber(bossInfo["stat_reset"]) == 1 then
            -- 出现了某个BOSS就需要重置统计
            count = reset;
        end
    end

    BuildingBonusM.setFieldStat(key, count);
end

-- 开始迷宫
startDungeon = function()
    if not ActivityDungeonM.isInActivityBossLayer() then
        -- 不是在BOSS层
        return;
    end

    -- BOSS层统计
    statEnterBossLayerStat();

    -- 获取参战冈布奥
    local slime = getCombatSlime();
    if slime then
        ME.user.chessBossAppearSummon = nil;

        -- 尝试触发出场召唤怪物
        local props = PropM.fetchProps(slime, "appear_do_summon");
        for _, prop in ipairs(props) do
            for i = 1, prop[3] do
                PropM.trigger(slime, prop[1], prop[2]);
            end
        end

        -- 召唤完就删掉部分字段
        ME.user.dbase:deleteTemp("fetch_chess_item");

        EventMgr.fire(event.SPECIAL_ENTER_EFFECT);
    end
end

-- BOSS死亡
bossDie = function()
    if not ActivityDungeonM.isInActivityBossLayer() then
        -- 不是在BOSS层
        return;
    end

    local door = CombatM.filterByClass(DungeonM.getCurrentDungeon(), GRID_TYPE_DOOR)[1];
    door.opened = 1;
end

-- 超导充能平台(给BOSS提供增益)
function energyPlatformTrigger()
    local grids = findEnergyPlatformGrids();
    if #grids <= 0 then
        -- 没有充能平台
        return;
    end

    local slime = getCombatSlime();
    if not slime or slime:isDead() then
        -- 对手史莱姆已经死亡
        return;
    end

    -- 玩家使用了时间停止
    local prop = PropM.combine(ME.user, "stop_action", 1);
    if PropM.apply(prop, 1) > 0 then
        -- 玩家使用了时间停止
        trace("ActivityBossM", "玩家使用了时间停止。");
        return;
    end

    for _, grid in pairs(grids) do
        local pos = grid:getPos();
        local skillId = grid.skill;
        local round = CombatM.getRound() - 1; -- 当前结束的回合数
        if getEnergyPlatformCd(pos, round) == 1 then
            -- 释放技能
            SkillM.apply(slime, slime, skillId, round);

            EventMgr.fire(event.ENERGY_PLATFORM_TRIGGER, {["pos"] = pos, ["target"] = slime});
        end
    end
end

-- 获取超导充能平台
function findEnergyPlatformGrids()
    local ret = {};

    -- 先取出所有格子
    local grids = DungeonM.getCurrentDungeon() or {};
    for _, grid in pairs(grids) do
        if grid.state ~= GRID_STATE_DISABLE and
            grid.type == GRID_TYPE_ENERGY_PLATFORM then
            table.insert(ret, grid);
        end
    end

    return ret;
end

-- 充能平台倒计时
function getEnergyPlatformCd(pos, round)
    local grid = DungeonM.getGridByPos(pos);

    local cd = grid.cd;
    round = round or CombatM.getRound();
    local leftCd = cd - round % cd + 1;
    leftCd = iif(leftCd > cd, 1, leftCd);

    return leftCd;
end